package tenglang.tool.gen.controller;

import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation;
import org.apache.commons.io.IOUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.EnvironmentAware;
import org.springframework.core.env.Environment;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.PreparedStatementCreator;
import org.springframework.jdbc.support.GeneratedKeyHolder;
import org.springframework.jdbc.support.KeyHolder;
import org.springframework.util.StreamUtils;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import tenglang.common.core.text.Convert;
import tenglang.common.utils.ZipUtils;
import tenglang.common.web.controller.BaseController;
import tenglang.common.web.domain.AjaxResult;
import tenglang.common.web.page.TableDataInfo;
import tenglang.tool.gen.config.GenConfig;
import tenglang.tool.gen.domain.GenTable;
import tenglang.tool.gen.domain.GenTableColumn;
import tenglang.tool.gen.mapper.GenTableMapper;
import tenglang.tool.gen.service.IGenTableColumnService;
import tenglang.tool.gen.service.IGenTableService;

import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.*;
import java.util.zip.ZipInputStream;

/**
 * 代码生成 操作处理
 * 
 * @author ruoyi
 */
@RestController
@RequestMapping("/tool/gen")
@Api(tags = "代码生成")
public class GenController extends BaseController implements EnvironmentAware
{

    @Autowired
    private IGenTableService genTableService;

    @Autowired
    private IGenTableColumnService genTableColumnService;


    @Autowired
    private GenTableMapper genTableMapper;

    @Autowired
    private JdbcTemplate jdbcTemplate;

    /**
     * 查询代码生成列表
     */
    @GetMapping("/list")
    public TableDataInfo genList()
    {
        GenTable genTable=new GenTable();
        startPage();
        List<GenTable> list = genTableService.selectGenTableList(genTable);
        return getDataTable(list);
    }

    /**
     * 修改代码生成业务
     */
    @GetMapping(value = "/{talbleId}")
    public AjaxResult getInfo(@PathVariable Long talbleId)
    {
        GenTable table = genTableService.selectGenTableById(talbleId);
        List<GenTableColumn> list = genTableColumnService.selectGenTableColumnListByTableId(talbleId);
        Map<String, Object> map = new HashMap<String, Object>();
        map.put("info", table);
        map.put("rows", list);
        return AjaxResult.success(map);
    }

    /**
     * 查询数据库列表
     */
    @GetMapping("/db/list")
    public TableDataInfo dataList(GenTable genTable)
    {
        startPage();
        List<GenTable> list = genTableService.selectDbTableList(genTable);
        return getDataTable(list);
    }

    /**
     * 查询数据表字段列表
     */
    @GetMapping(value = "/column/{talbleId}")
    public TableDataInfo columnList(Long tableId)
    {
        TableDataInfo dataInfo = new TableDataInfo();
        List<GenTableColumn> list = genTableColumnService.selectGenTableColumnListByTableId(tableId);
        dataInfo.setRows(list);
        dataInfo.setTotal(list.size());
        return dataInfo;
    }

    /**
     * 导入表结构（保存）
     */
    @PostMapping("/importTable")
    public AjaxResult importTableSave(String tables)
    {
        String[] tableNames = Convert.toStrArray(tables);
        // 查询表信息
        List<GenTable> tableList = genTableService.selectDbTableListByNames(tableNames);
        genTableService.importGenTable(tableList);
        return AjaxResult.success();
    }

    /**
     * 修改保存代码生成业务
     */
    @PutMapping
    public AjaxResult editSave(@Validated @RequestBody GenTable genTable)
    {
        System.out.println(genTable.getParams().size());
        genTableService.validateEdit(genTable);
        genTableService.updateGenTable(genTable);
        return AjaxResult.success();
    }

    @DeleteMapping("/{tableIds}")
    public AjaxResult remove(@PathVariable Long[] tableIds)
    {
        genTableService.deleteGenTableByIds(tableIds);
        return AjaxResult.success();
    }

    /**
     * 预览代码
     */
    @GetMapping("/preview/{tableId}")
    public AjaxResult preview(@PathVariable("tableId") Long tableId) throws IOException
    {
        Map<String, String> dataMap = genTableService.previewCode(tableId);
        return AjaxResult.success(dataMap);
    }

    /**
     * 生成代码
     */
    @GetMapping("/genCode/{tableName}")
    public void genCode(HttpServletResponse response, @PathVariable("tableName") String tableName) throws IOException
    {
        byte[] data = genTableService.generatorCode(tableName,null);
        genCode(response, data);
    }

    /**
     * 批量生成代码
     */
    @GetMapping("/batchGenCode")
    public void batchGenCode(HttpServletResponse response, String tables) throws IOException
    {
        String[] tableNames = Convert.toStrArray(tables);
        byte[] data = genTableService.generatorCode(tableNames,null);
        genCode(response, data);
    }


    /**
     * 批量代码导入
     */
    @GetMapping("/batchGenCodeImport")
    @ApiOperation("代码导入到项目中")
    public void batchGenCodeImport(GenConfig genConfig) throws Exception
    {
        String[] tableNames = Convert.toStrArray(genConfig.getTables());
//        byte[] data = genTableService.generatorCode(tableNames);
        for(String tableName:tableNames){
            // 查询表信息
            GenTable table = genTableMapper.selectGenTableByName(tableName);

            byte[] data =genTableService.generatorCode(tableName,genConfig.getSystemPrefix());
            InputStream arrInput=new ByteArrayInputStream(data);
            ZipUtils zipUtils=new ZipUtils(arrInput);

            createJavaItem(GenConfig.controllerName,table,zipUtils,genConfig);
            createJavaItem(GenConfig.serviceImplName,table,zipUtils,genConfig);
            createJavaItem(GenConfig.serviceName,table,zipUtils,genConfig);
            createJavaItem(GenConfig.domainName,table,zipUtils,genConfig);
            createJavaItem(GenConfig.mapperName,table,zipUtils,genConfig);

            createResourceItem(GenConfig.mybatisXml,table,zipUtils,genConfig);


            createViewItem(table,zipUtils,genConfig);
            createApiItem(GenConfig.uiApiSrc,zipUtils,table,genConfig);
            createMockItem(GenConfig.uiMockSrc,zipUtils,table,genConfig);

            createSQLItem(zipUtils,table,genConfig);
            IOUtils.closeQuietly(arrInput);
        }
    }

    /**
     * 生成zip文件
     */
    private void genCode(HttpServletResponse response, byte[] data) throws IOException
    {
        response.reset();
        response.setHeader("Content-Disposition", "attachment; filename=\"ruoyi.zip\"");
        response.addHeader("Content-Length", "" + data.length);
        response.setContentType("application/octet-stream; charset=UTF-8");
        IOUtils.write(data, response.getOutputStream());
    }

    public void createJavaItem(String typePackage,GenTable table, ZipUtils zipUtils,GenConfig genConfig) throws IOException {
        File javaModule=createjavaModule(genConfig,table);
        File javaModulePackage=new File(javaModule,typePackage);
        if(!javaModulePackage.exists()){
            javaModulePackage.mkdirs();
        }
        String zipJavaRoot= "main/java/";
        String packageName= table.getPackageName();
        packageName=packageName.replaceAll("\\.","/");
        Map<String,byte[]> files=zipUtils.filterKey(zipJavaRoot+packageName+'/'+typePackage+'/');
        Iterator<Map.Entry<String,byte[]>> entryiterator=files.entrySet().iterator();
        while (entryiterator.hasNext()){
            Map.Entry<String,byte[]> entry=entryiterator.next();
            File javaModuleFile=new File(javaModulePackage,entry.getKey());
            if(!javaModuleFile.exists()){
                javaModuleFile.createNewFile();
            }
            OutputStream foutput=new FileOutputStream(javaModuleFile);
            StreamUtils.copy(entry.getValue(),foutput);
            IOUtils.closeQuietly(foutput);
        }
    }


    public void createResourceItem(String typePackage,GenTable table,ZipUtils zipUtils,GenConfig genConfig) throws IOException {
        File resourceModule=createResourceModule(genConfig);
        String zipJavaRoot= "main/resources/"+typePackage;
        String[] packageName=table.getPackageName().split("\\.");

        File resourceModulePackage=new File(resourceModule,typePackage+'/'+packageName[packageName.length-1]+'/');
        if(!resourceModulePackage.exists()){
            resourceModulePackage.mkdirs();
        }
        Map<String,byte[]> files=zipUtils.filterKey(zipJavaRoot+'/'+packageName[packageName.length-1]+'/');
        Iterator<Map.Entry<String,byte[]>> entryiterator=files.entrySet().iterator();
        while (entryiterator.hasNext()){
            Map.Entry<String,byte[]> entry=entryiterator.next();
            File resourceModuleFile=new File(resourceModulePackage,entry.getKey());
            if(!resourceModuleFile.exists()){
                resourceModuleFile.createNewFile();
            }
            OutputStream foutput=new FileOutputStream(resourceModuleFile);
            StreamUtils.copy(entry.getValue(),foutput);
            IOUtils.closeQuietly(foutput);
        }

    }


    public void createViewItem(GenTable table,ZipUtils zipUtils,GenConfig genConfig) throws IOException {
          File uiModule=createUiModule(genConfig,table);
          String zipVueRoot= "vue/views";
          String[] packageName=table.getPackageName().split("\\.");
          Map<String,byte[]> files=zipUtils.filterKey(zipVueRoot+'/'+packageName[packageName.length-1]+'/'+table.getBusinessName()+'/');
          Iterator<Map.Entry<String,byte[]>> entryiterator=files.entrySet().iterator();
          while (entryiterator.hasNext()){
              Map.Entry<String,byte[]> entry=entryiterator.next();
              File vueModuleFile=new File(uiModule,entry.getKey());
              if(!vueModuleFile.exists()){
                  vueModuleFile.createNewFile();
              }
              OutputStream foutput=new FileOutputStream(vueModuleFile);
              StreamUtils.copy(entry.getValue(),foutput);
              IOUtils.closeQuietly(foutput);
          }
    }

    public void createApiItem(String typePackage,ZipUtils zipUtils,GenTable table,GenConfig genConfig) throws IOException {
        File apiModule=createApiModule(genConfig,table);
        String zipVueRoot= "vue/api/";
        String[] packageName=table.getPackageName().split("\\.");
        Map<String,byte[]> files=zipUtils.filterKey(zipVueRoot+packageName[packageName.length-1]+'/');
        Iterator<Map.Entry<String,byte[]>> entryiterator=files.entrySet().iterator();
        while (entryiterator.hasNext()){
            Map.Entry<String,byte[]> entry=entryiterator.next();
            File apiModuleFile=new File(apiModule,entry.getKey());
            if(!apiModuleFile.exists()){
                apiModuleFile.createNewFile();
            }
            OutputStream foutput=new FileOutputStream(apiModuleFile);
            StreamUtils.copy(entry.getValue(),foutput);
            IOUtils.closeQuietly(foutput);
        }
    }


    public void createMockItem(String typePackage,ZipUtils zipUtils,GenTable table,GenConfig genConfig) throws IOException {
        File mockModule=createMockModule(genConfig,table);
        String zipVueRoot= "vue/mock/";
        String[] packageName=table.getPackageName().split("\\.");
        Map<String,byte[]> files=zipUtils.filterKey(zipVueRoot+packageName[packageName.length-1]+'/');
        Iterator<Map.Entry<String,byte[]>> entryiterator=files.entrySet().iterator();
        while (entryiterator.hasNext()){
            Map.Entry<String,byte[]> entry=entryiterator.next();
            File mockModuleFile=new File(mockModule,entry.getKey());
            if(!mockModuleFile.exists()){
                mockModuleFile.createNewFile();
            }
            OutputStream foutput=new FileOutputStream(mockModuleFile);
            StreamUtils.copy(entry.getValue(),foutput);
            IOUtils.closeQuietly(foutput);
        }
    }

    public void createSQLItem(ZipUtils zipUtils,GenTable table,GenConfig genConfig) throws Exception{
        String zipSQLRoot= ".sql";
        Map<String,byte[]> files=zipUtils.filterKeyEnd(zipSQLRoot);
        Iterator<Map.Entry<String,byte[]>> entryiterator=files.entrySet().iterator();
        while (entryiterator.hasNext()){
            Map.Entry<String,byte[]> entry=entryiterator.next();
            BufferedReader bufferedReader=new BufferedReader(new InputStreamReader(new ByteArrayInputStream(entry.getValue()),"utf-8"));
            List<String> sqlSet=new ArrayList<>();
            String line=null;
            while((line=bufferedReader.readLine())!=null){
                sqlSet.add(line);
            }
            KeyHolder keyHolder = new GeneratedKeyHolder();
            String first=sqlSet.get(0);
            jdbcTemplate.update(new PreparedStatementCreator() {
                @Override
                public PreparedStatement createPreparedStatement(Connection conn)
                        throws SQLException {
                    return conn.prepareStatement(first.trim().substring(first.trim().indexOf('i')),Statement.RETURN_GENERATED_KEYS);
                }
            },keyHolder);
            Number number=keyHolder.getKey();
            for(int a=1;a<sqlSet.size();a++){
                String sql=sqlSet.get(a).trim().substring(sqlSet.get(a).trim().indexOf('i'));
                sql=sqlSet.get(a).replace("@parentId",String.valueOf(keyHolder.getKey()));
                jdbcTemplate.update(sql);
            }
        }
    }

    private  File createjavaModule(GenConfig genConfig,GenTable genTable){
        String packageName=genTable.getPackageName();
        packageName=packageName.replaceAll("\\.","\\\\");
        File javaModule=new File(genConfig.getProjectDir()+GenConfig.mainSrc+packageName+"\\");
        if(!javaModule.exists()){
            javaModule.mkdirs();
        }
        return javaModule;
    }


    private  File createResourceModule(GenConfig genConfig){
        File resourceModule=new File(genConfig.getProjectDir()+GenConfig.mainResource+"\\");
        if(!resourceModule.exists()){
            resourceModule.mkdirs();
        }
        return resourceModule;
    }


    private  File createUiModule(GenConfig genConfig,GenTable genTable){
        String[] packageNames=genTable.getPackageName().split("\\.");
        File uiModule=new File(genConfig.getUiProjectDir()+GenConfig.uiMainSrc+packageNames[packageNames.length-1]+"\\"+genTable.getBusinessName()+"\\");
        if(!uiModule.exists()){
            uiModule.mkdirs();
        }
        return uiModule;
    }



    private  File createApiModule(GenConfig genConfig,GenTable genTable){
        String[] packageNames=genTable.getPackageName().split("\\.");
        File uiModule=new File(genConfig.getUiProjectDir()+GenConfig.uiApiSrc+packageNames[packageNames.length-1]+"\\");
        if(!uiModule.exists()){
            uiModule.mkdirs();
        }
        return uiModule;
    }

    private  File createMockModule(GenConfig genConfig,GenTable genTable){
        String[] packageNames=genTable.getPackageName().split("\\.");
        File uiModule=new File(genConfig.getUiProjectDir()+GenConfig.uiMockSrc+"\\"+packageNames[packageNames.length-1]+"\\");
        if(!uiModule.exists()){
            uiModule.mkdirs();
        }
        return uiModule;
    }



    /**
     * Set the {@code Environment} that this component runs in.
     *
     * @param environment
     */
    @Override
    public void setEnvironment(Environment environment) {
        GenConfig.autoRemovePre=environment.getProperty("gen.autoRemovePre",Boolean.class,true);
        GenConfig.tablePrefix=environment.getProperty("gen.tablePrefix");
        GenConfig.packageName=environment.getProperty("gen.packageName");
    }
}